package oop.ex1.filescript;
import java.io.*;
import java.util.*;

import oop.ex1.filescript.MyFileScript.FiltersNames;
import oop.ex1.filescript.MyFileScript.OrderNames;
import oop.ex1.filters.*;
import oop.ex1.orders.*;
/**
 * a class that converts a command file into blocks while separating each block to Filters,Actions,Orders and Comments.
 * @author danielshalev
 * @author danalmog
 */
public class BlockParser{
	private  ArrayList<Blocks> blocks=new ArrayList<Blocks>();
	/**
	 * an enum thats containes the names and chars that allowd in the commends file
	 * @author danalmog
	 * @author danielshalev
	 */
	public enum lineStart{
		FILTER("FILTER"),
		ACTION("ACTION"),
		ORDER("ORDER"),
		COMMENT("$"),
		SPACE(" ");
		
		private final String type;
		lineStart(String type){
			this.type=type;
		}
	}
	/**
	 * construct an newBlockPareser and parse the file.
	 * @param file the file to parse
	 * @throws BadFilterExeption indicates if there is a bad filter in the command file
	 * @throws FilterDateExeption indicates if the parameter that was entered is not a date
	 * @throws FilterYesNoExeption indicates if the parameter that was entered is not yes/no 
	 * @throws BadCommandFileExeption indicates if there is a general problem with the command file 
	 * @throws EmptyCommandFileExeption indicates if the command file isn't empty 
	 */
	public BlockParser(File file) throws BadFilterExeption, FilterDateExeption, FilterYesNoExeption, BadCommandFileExeption, EmptyCommandFileExeption{
		parse(file);
	}
	
	/**
	 * reads the command file and saves each line in an arraylist.
	 * @param commands the file to read
	 * @throws EmptyCommandFileExeption indicates if the command file isn't empty 
	 */
	public ArrayList<String> fileReader(File commands) throws EmptyCommandFileExeption{
		ArrayList <String> fileLines;
		try{
			
			fileLines =new ArrayList<String>();
			//creates a new BufferedReader for the commaneds file
			BufferedReader reader=new BufferedReader(new FileReader(commands));
			String line;
			//reads the file line by line
			while((line=reader.readLine())!=null){	
				fileLines.add(line);
			}	
			fileLines.trimToSize();
		}
		catch(IOException ioe){
			throw new EmptyCommandFileExeption();
		}
		return fileLines;
		
	}
	/**
	 * 	 * a method that gets a commend file and separates it to blocks.
	 * @param commands the file to separate
	 * @throws FilterYesNoExeption indicates if the parameter that was entered is not yes/no 
	 * @throws BadFilterExeption indicates if there is a bad filter in the command file
	 * @throws FilterDateExeption indicates if the parameter that was entered is not a date
	 * @throws BadCommandFileExeption indicates if there is a general problem with the command file 
	 * @throws EmptyCommandFileExeption indicates if the command file isn't empty 
	 */
	public void parse(File commands) throws FilterYesNoExeption, BadFilterExeption, FilterDateExeption, BadCommandFileExeption, EmptyCommandFileExeption{	
		ArrayList <String> lines=fileReader(commands);				
		Iterator<String> iter=lines.iterator();
		String next=new String();
		//checks if the command file start's with FILTER 
		if(!(lines.get(0).equals(lineStart.FILTER.type)) || !iter.hasNext())
			throw new BadCommandFileExeption();
		iter.next();
		//run's all over the lines of the commend block(stored in arrayList)
		while(iter.hasNext() && !(next=iter.next()).equals(lineStart.FILTER.type)){
			
			Blocks tempBlock=new Blocks();
			boolean hasAction=false;
			boolean hasFliter=false;
			boolean hasOrder=false;
			//add filters to the block
			while(!(next.equals(lineStart.ACTION.type))){
				if(next.equals(lineStart.ORDER.type))
					throw new BadCommandFileExeption();
				if (next.startsWith(lineStart.COMMENT.type))
					//add a comment
					tempBlock.addComment(next);
				else {
					addFilter(next.split(" "),tempBlock);
					//indicates that there is at least one filter
					hasFliter=true;
				}
				next=iter.next();
			}
			
			//add actions to the block
			while(iter.hasNext() && !(next=iter.next()).equals(lineStart.ORDER.type) && !(next.equals(lineStart.FILTER.type))){
				if (next.startsWith(lineStart.COMMENT.type))
					tempBlock.addComment(next);
				else {
					tempBlock.addAction(next);
					//indicates that there is at least one action
					hasAction=true;
				}
			}
			//add orders to the block
			while(iter.hasNext() && !((next=iter.next()).equals(lineStart.FILTER.type))){
				if (next.startsWith(lineStart.COMMENT.type))
					tempBlock.addComment(next);
				else if(!(next.equals(lineStart.FILTER.type))){
					if(hasOrder)
						throw new BadCommandFileExeption();
					addOredr(next, tempBlock);
					//indicates that there is an order
					hasOrder=true;
				}
			}
			//adds an order if Necessary
			if(!hasOrder){
				tempBlock.setOrder(new AbsOrder());
			}
			//throws an exception if there is no filter or no action
			if(!hasAction || !hasFliter)
				throw new BadCommandFileExeption();
			//add's the block
			blocks.add(tempBlock);
		}//end of while
	}
	/**
	 * a method that rerturns the blocks list
	 * @return the blocks that where made from the command file 
	 * @throws EmptyCommandFileExeption indicates if the command file isn't empty 
	 */
	public ArrayList<Blocks> getBlocks() throws EmptyCommandFileExeption {
		if(blocks==null)
			throw new EmptyCommandFileExeption();
		blocks.trimToSize();
		return blocks;
	}
	
	/**
	 * a methods that gets block and string array of filters and adds filters to the block
	 * @param StringFilters an array of filters saved as strings 
	 * @param tempBlock the block to save the filters in
	 * @throws FilterDateExeption indicates if the parameter that was entered is not a date
	 * @throws BadFilterExeption indicates if there is a bad filter in the command file
	 * @throws FilterYesNoExeption indicates if the parameter that was entered is not yes/no 
	 */
	private void addFilter(String[] StringFilters, Blocks tempBlock) throws FilterDateExeption, BadFilterExeption, FilterYesNoExeption{
		try{
			Filters[] filters=new Filters[StringFilters.length];
			//run all over the filters array
			for(int i=0;i<StringFilters.length;i++){
				boolean hasNot=false;
				String argument;
				//check if the filter should be a NOT filter
				if(StringFilters[i].split("%").length==3){
					hasNot=true;
				}
				argument=new String(StringFilters[i].split("%")[1]);
				FiltersNames name=FiltersNames.valueOf(StringFilters[i].split("%")[0]);
				//check what filter is it and adds it to an array
				switch (name) {
				case after:
					filters[i]=new After(argument,hasNot);
					break;				
				case before:
					filters[i]=new Before(argument,hasNot);
					break;			
				case greater_than:
					filters[i]=new Greater(argument,hasNot);
					break;		
				case smaller_than:
					filters[i]=new Smaller(argument,hasNot);
					break;
				case file:
					filters[i]=new FileName(argument,hasNot);
					break;
				case prefix:
					filters[i]=new Prefix(argument,hasNot);
					break;
				case suffix:
					filters[i]=new Suffix(argument,hasNot);
					break;
				case writable:
					filters[i]=new Writable(argument,hasNot);
					break;
				case executable:
					filters[i]=new Executable(argument,hasNot);
					break;
				case hidden:
					filters[i]=new Hidden(argument,hasNot);
					break;
					
				default:
					throw new BadFilterExeption();
				}	
			}
			tempBlock.addFilter(filters);
		}
		catch(java.lang.IllegalArgumentException e){
			throw new BadFilterExeption();
		}
	}

	/**
	 * a method that gets a block and adds an order to it 
	 * @param stringOredr the order to add
	 * @param tempBlock the block we want to add the string to
	 * @throws BadOrderExeption indicates that there is a bad order in the command file
	 */
	private void addOredr(String stringOredr,Blocks tempBlock) throws BadOrderExeption{
		try{
			//sets the default order as abs
			if(stringOredr==null){
				tempBlock.setOrder(new AbsOrder());
				return;
			}
			//adds an order
			OrderNames orderNames=OrderNames.valueOf(stringOredr);
			switch (orderNames) {
			case abs:
				tempBlock.setOrder(new AbsOrder());
				break;
			case file:
				tempBlock.setOrder(new FileOrder());
				break;
			case mod:
				tempBlock.setOrder(new ModOrder());
				break;
			case size:
				tempBlock.setOrder(new SizeOrder());
				break;
			default:
				throw new BadOrderExeption();
			}
		}
		catch(IllegalArgumentException e){
			throw new BadOrderExeption();
		}
	}


}
